import java.util.*;

public class EncryptClass {

    private byte[] cipher;
    private byte[] text;
    private byte[] IV;
    private String type;
    private boolean encrypt;
    private byte[] integrityKey;
    private byte[] encryptionKey;

    public EncryptClass(String itype, byte[] encKey, byte[] intKey, byte[] iIV, byte[] input, boolean iencrypt) throws Exception {
        byte[] newInput;

        if (input.length % 8 == 0) {
            newInput = new byte[input.length];
            newInput = input;
            //then good
        } else {
            int rem = input.length % 8;
            newInput = new byte[input.length + 8 - rem];
            for (int i = 0; i < input.length; i++) {
                newInput[i] = input[i];
            }
            for (int i = 0; i < (8 - rem); i++) {
                newInput[input.length + i] = 0x00;
            }
        }

        this.cipher = new byte[newInput.length];
        this.text = new byte[newInput.length];
        this.IV = new byte[iIV.length];
        this.encryptionKey = new byte[encKey.length];
        this.encryptionKey = encKey;
        this.integrityKey = new byte[intKey.length];
        this.integrityKey = intKey;
        this.encrypt = iencrypt;
        this.IV = iIV;
        this.type = itype;


        if (this.encrypt) {
            this.text = newInput;
        } else {
            this.cipher = newInput;
        }


        //do 3DES/CFB
        if (type.equals("CFB")) {
            System.out.println("Encryption Type: 3DES/CFB");

            CFB cfb = new CFB();

            if (this.encrypt) {
                cfb.k = this.text.length; // size of input to be encrypted
                this.cipher = cfb.Encrypt(this.encryptionKey, this.IV, this.text);
                System.out.println("Encrypted Text: " + Utils.ToHexString(this.cipher));
            } else {  // decrypt
                cfb.k = this.cipher.length; // size of input to be encrypted
                this.text = cfb.Decrypt(this.encryptionKey, this.IV, this.cipher);
                System.out.println("Decrypted Text: " + Utils.ByteToString(this.text));
            }

        } //do 3DES/CBC
        else if (type.equals("CBC")) {
            System.out.println("Encryption Type: 3DES/CBC");

            CBC cbc = new CBC();
            byte[] MAC;
            if (this.encrypt) {

                MAC = cbc.GetMac(this.integrityKey, this.IV, this.text);
				
				//join text and mac
				byte[] temp = new byte[this.text.length + MAC.length];
				System.arraycopy(this.text, 0, temp, 0, this.text.length);
				System.arraycopy(MAC, 0, temp, this.text.length, MAC.length);
                this.cipher = temp; //CBC is used for integrity only so no need to encrypt text
				
                System.out.println("MAC for sequence: " + Utils.ToHexString(MAC));
                System.out.println("Text: " + Utils.ByteToString(this.text));

            } else {
               
                this.text = Arrays.copyOfRange(this.cipher, 0, this.cipher.length - 8);
                MAC = cbc.GetMac(this.integrityKey, this.IV, this.text);
				byte[] receivedMAC = Arrays.copyOfRange(this.cipher, this.cipher.length - 8, this.cipher.length);
                 
				System.out.println("Text: " + Utils.ByteToString(this.text)); 
				System.out.println("MAC for sequence: " + Utils.ToHexString(MAC)); 
                System.out.println("Received MAC for sequence: " + Utils.ToHexString(receivedMAC));
                
                
                boolean match = Utils.ToHexString(MAC).equals(Utils.ToHexString(receivedMAC));
                if (match) {
                    System.out.println("The MACs are equal");
                } else {
                    System.out.println("The MACs are NOT equal");
                }
            }

        } //do 3DES/PCBC
        else if (type.equals("PCBC")) {
            System.out.println("Encryption Type: 3DES/PCBC");

            PCBC pcbc = new PCBC();

            if (this.encrypt) {
                this.cipher = pcbc.Encrypt(this.encryptionKey, IV, input);
                System.out.println("Encrypted Text: " + Utils.ToHexString(this.cipher));
            } else {
                boolean[] b = new boolean[1];
                this.text = pcbc.Decrypt(this.encryptionKey, IV, cipher, b);
                System.out.println("Decrypted Text: " + Utils.ByteToString(this.text));

                if (b[0]) {
                    System.out.println("Cipher text was NOT modified");
                } else {
                    System.out.println("Cipher text was modified");
                }
            }


        } //do RC4
        else if (type.equals("RC4")) {

            if (this.encrypt) {
                RC4 rc4 = new RC4(this.getText(), true, this.encryptionKey);
                this.cipher = rc4.getCipherBytes();
                System.out.println("Encrypted Text: " + Utils.ToHexString(this.cipher));
            } else {
                RC4 rc4 = new RC4(this.getCipher(), false, this.encryptionKey);
                this.text = rc4.getPlainBytes();
                System.out.println("Decrypted Text: " + Utils.ByteToString(this.text));
            }
        }

    }

    /**
     * @return the cipher
     */
    public byte[] getCipher() {
        return cipher;
    }

    /**
     * @return the text
     */
    public byte[] getText() {
        return text;
    }
}
